﻿/**
 * PvZ Tool
 * 
 * @classname   Save
 * @description For reading and writing changes to save game
 * @author      Martomo
 * @author      Kamizoom
 * @version     0.0.2
 * 
 */
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace PvZTool
{

    public class Save
    {
        // Default size of save (without plants in Zen Garden)
        public const long defaultLength = 1777;
        // Size of current loaded save
        public static long saveLength = -1;
        // Path location of save file
        public static string saveFile;
        // Buffer streams for loaded save
        public static FileStream fileData;
        public static BinaryReader saveData;
        public static BinaryWriter writeData;

        /**
         * Dictionary for keeping track of changes that are going to be made to save
         * saveChanges {Dictionary} Key: Offset || Value: Value of offset (needs to be integer, 0x****)
         */
        public static Dictionary<int, int> saveChanges = new Dictionary<int, int>();

        /**
         * Add/update change to saveChanges
         * 
         * @param changeOffset
         * @param changeValue
         * @return void
         */
        public static void SetChange(int changeOffset, int changeValue)
        {
            // Check if this value is already added, then update entry
            if (saveChanges.ContainsKey(changeOffset))
            {
                saveChanges[changeOffset] = changeValue;
            }
            // If it does not exist add new entry to saveChanges
            else
            {
                saveChanges.Add(changeOffset, changeValue);
            }
        }

        /**
         * Returns value from current loaded save
         * 
         * @param posValue
         * @param bitSize
         * @param bigEndian
         * @return string
         */
        public static int GetValue(int posValue, int bitSize = 8, bool bigEndian = true)
        {
            if (saveData != null)
            {
                saveData.BaseStream.Position = posValue;
                byte[] readBuffer;
                switch (bitSize)
                {
                    case 8:
                        return saveData.ReadByte();
                    case 16:
                        readBuffer = saveData.ReadBytes(2);
                        if (bigEndian == false) { Array.Reverse(readBuffer); }
                        return BitConverter.ToInt16(readBuffer, 0);
                    case 32:
                    default:
                        readBuffer = saveData.ReadBytes(4);
                        if (bigEndian == false) { Array.Reverse(readBuffer); }
                        return BitConverter.ToInt16(readBuffer, 0);
                }
            }
            else
            {
                return -1;
            }
        }

        /**
         * Open save
         * 
         * @return bool
         */
        public static bool Open()
        {
            bool openResult = false;

            // Show a file dialog, so user can browse/open their save file
            OpenFileDialog OFD = new OpenFileDialog();
            OFD.Filter = "PvZ savegame|*.dat";
            DialogResult OFDresult = OFD.ShowDialog();

            // Check if file was succesfully opened/selected
            if (OFDresult == DialogResult.OK)
            {
                // Update path location of save file
                saveFile = OFD.FileName;
                Debug.WriteLine("Opening file: " + saveFile);
                try
                {
                    fileData = new FileStream(saveFile, FileMode.Open, FileAccess.ReadWrite);
                    // Check if save has at least minimum filesize
                    if (fileData.Length >= defaultLength)
                    {
                        // Change window title to current file
                        Tool.ActiveForm.Text = Program.appName + " - " + OFD.SafeFileName;

                        // Open file streams
                        saveData = new BinaryReader(fileData);
                        writeData = new BinaryWriter(fileData);

                        saveLength = fileData.Length;
                        openResult = true;
                    }
                    else
                    {
                        Function.MsgBox("File size is to small!");
                        fileData.Close();
                        fileData = null;
                        saveFile = null;
                    }
                }
                catch (IOException)
                {
                    Function.MsgBox("Unable to open/read file!");
                }
            }

            return openResult;
        }

        /**
         * Save changes to save
         * 
         * @return bool
         */
        public static bool Write()
        {
            bool writeResult = false;
            string noSaveMsg = "Nothing to save.\nNo changes have been made.";
            if (saveChanges != null)
            {
                Debug.WriteLine("saveChanges Length: " + saveChanges.Count);
                if (saveChanges.Count > 0)
                {
                    try
                    {
                        int changeIndex = 1;
                        foreach (KeyValuePair<int, int> change in saveChanges)
                        {
                            // Output
                            Debug.WriteLine("Change #{0}: {1}, {2}",
                            changeIndex,
                            change.Key.ToString("X"),
                            change.Value.ToString("X"));
                            changeIndex++;

                            // Write changes to save
                            writeData.BaseStream.Position = change.Key;
                            // Value needs to be integer, 0x****
                            writeData.Write(change.Value);
                        }
                        saveChanges.Clear();
                        // Make sure all data will finish writing to save file
                        writeData.Flush();
                    }
                    catch (IOException)
                    {
                        Function.MsgBox("Unable to save changes to file!");
                    }
                    finally
                    {
                        writeResult = true;
                    }
                }
                else
                {
                    Function.MsgBox(noSaveMsg);
                }
            }
            else
            {
                Function.MsgBox(noSaveMsg); 
            }
            return writeResult;
        }

        /**
         * Close save
         * 
         * @return void
         */
        public static void Close()
        {
            // Close file streams we don't need anymore
            fileData.Close();
            saveData.Close();
            writeData.Close();
            // Reset variables
            fileData = null;
            saveData = null;
            saveFile = null;
            saveLength = -1;
            writeData = null;
        }

    }

}
